package com.ly.heart.service.impl;

import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.ly.common.PageParam;
import com.ly.common.core.domain.AjaxResult;
import com.ly.common.core.domain.entity.SysDept;
import com.ly.common.core.domain.entity.SysUser;
import com.ly.common.exception.CustomException;
import com.ly.common.utils.DateUtils;
import com.ly.common.utils.SecurityUtils;
import com.ly.common.utils.StringUtils;
import com.ly.common.utils.erp.*;
import com.ly.heart.domain.*;
import com.ly.heart.domain.dto.KingInvoicePayableDto;
import com.ly.heart.domain.dto.StartProcessDto;
import com.ly.common.core.domain.ActCurrentTaskVo;
import com.ly.heart.domain.vo.KingInvoicePayableMenuVo;
import com.ly.heart.domain.vo.KingInvoicePayableVo;
import com.ly.heart.mapper.KingInvoicePayableMapper;
import com.ly.heart.mapper.MaterialDetailMapper;
import com.ly.heart.service.*;
import com.ly.heart.service.king.PayablePushService;
import com.ly.heart.util.ChineseToPinyin;
import com.ly.heart.util.CurrentUserUtil;
import com.ly.system.service.ISysDeptService;
import com.ly.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;

/**
 * 金蝶-发票-应付单/采购开票Service业务层处理
 * @author wutao
 * @date 2024-06-26
 */
@Service
@Slf4j
public class KingInvoicePayableServiceImpl extends ServiceImpl<KingInvoicePayableMapper, KingInvoicePayableEntity> implements IKingInvoicePayableService {

    @Resource
    private KingInvoicePayableMapper kingInvoicePayableEntityMapper;
    @Resource
    private IKingInvoicePayableDetailService iKingInvoicePayableDetailService;
    @Resource
    private IKingInvoicePayableProcurePlanService iKingInvoicePayableProcurePlanService;
    @Resource
    private MaterialDetailMapper materialDetailMapper;
    @Resource
    private IKingProcureRefundMaterialService iKingProcureRefundMaterialService;
    @Resource
    private IKingProcureRefundService iKingProcureRefundService;
    @Resource
    private IActModelService iActModelService;
    @Resource
    private IKingInventoryMaterialService iKingInventoryMaterialService;
    @Resource
    private IKingInventoryService iKingInventoryService;
    @Resource
    private IOrderService iOrderService;
    @Resource
    private IMaterialDetailService iMaterialDetailService;
    @Resource
    private IKingDiffLogService iKingDiffLogService;
    @Resource
    private IKingCostAdjustmentService iKingCostAdjustmentService;
    @Resource
    private ISysDeptService iSysDeptService;
    @Resource
    private ISysUserService iSysUserService;
    @Resource
    private ISupplierService iSupplierService;
    @Resource
    private IKingPayableReceivableVerificationDetailService iKingPayableReceivableVerificationDetailService;
    @Resource
    private IActTaskCommentService iActTaskCommentService;
    @Resource
    private PayablePushService payablePushService;
    @Resource
    private IKingBaseCurrencyService iKingBaseCurrencyService;
    @Resource
    private IKingBaseSettlementTypeService iKingBaseSettlementTypeService;


    /**
     * 查询金蝶-发票-应付单/采购开票
     * @param id 金蝶-发票-应付单/采购开票ID
     * @return 金蝶-发票-应付单/采购开票
     */
    @Override
    public KingInvoicePayableVo selectKingInvoicePayableEntityById (String id) {
        KingInvoicePayableVo vo = new KingInvoicePayableVo ();
        KingInvoicePayableEntity payableEntity = kingInvoicePayableEntityMapper.selectKingInvoicePayableEntityById ( id );
        //查询应付单的明细
        KingInvoicePayableDetailEntity kingInvoicePayableDetailEntity = new KingInvoicePayableDetailEntity();
        kingInvoicePayableDetailEntity.setInvoicePayableId ( id );
        List<KingInvoicePayableDetailEntity> payableDetailEntities = iKingInvoicePayableDetailService.selectKingInvoicePayableDetailEntityList ( kingInvoicePayableDetailEntity );
        //查询应付单付款计划
        List<KingInvoicePayableProcurePlanEntity> payableProcurePlanEntities = iKingInvoicePayableProcurePlanService.list ( new QueryWrapper<KingInvoicePayableProcurePlanEntity> ().eq ( "invoice_payable_id", id ) );
        vo.setKingInvoicePayableEntity ( payableEntity ).setKingInvoicePayableDetailEntities ( payableDetailEntities ).setKingInvoicePayableProcurePlanEntities ( payableProcurePlanEntities );
        return vo;
    }

    /**
     * 根据Id查询单个对象
     * @param id
     * @return
     */
    @Override
    public KingInvoicePayableEntity querySingleById (String id) {
        KingInvoicePayableEntity payableEntity = kingInvoicePayableEntityMapper.selectKingInvoicePayableEntityById ( id );
        return payableEntity;
    }

    /**
     * 查询金蝶-发票-应付单/采购开票列表
     * 应付单列表合并，当选择为合并时，将退料和入库冲抵
     * @param kingInvoicePayableEntity 金蝶-发票-应付单/采购开票
     * @return 金蝶-发票-应付单/采购开票
     */
    @Override
    public List<KingInvoicePayableEntity> selectKingInvoicePayableEntityList (KingInvoicePayableEntity kingInvoicePayableEntity) {
        kingInvoicePayableEntity.setPage ( ( kingInvoicePayableEntity.getPage () - 1 ) * kingInvoicePayableEntity.getLimit () );
        kingInvoicePayableEntity.setIsUsed ( ConstUtil.IS_USED_YES );
        List<KingInvoicePayableEntity> kingInvoicePayableEntities = kingInvoicePayableEntityMapper.selectKingInvoicePayableEntityList ( kingInvoicePayableEntity );
        if ( ! CollectionUtils.isEmpty ( kingInvoicePayableEntities ) ) {
            List<String> docNos = kingInvoicePayableEntities.stream ().map ( KingInvoicePayableEntity :: getDocNo ).distinct ().collect ( Collectors.toList () );
            // 查询应付单的核销金额
            List<KingPayableReceivableVerificationDetailEntity> list = iKingPayableReceivableVerificationDetailService.list ( new LambdaQueryWrapper<KingPayableReceivableVerificationDetailEntity> ().in ( KingPayableReceivableVerificationDetailEntity :: getDocNo, docNos ) );
            kingInvoicePayableEntities.forEach ( payable -> {
                // 该应付单已核销总金额
                double sum = list.stream ().filter ( verify -> verify.getDocNo ().equals ( payable.getDocNo () ) ).mapToDouble ( verify -> Double.parseDouble ( verify.getVerificationAmount () ) ).sum ();
                if ( sum == 0 ) {
                    payable.setVerifyStatus ( "未核销" );
                } else if ( sum > 0 && sum < Double.parseDouble ( payable.getTotalTaxPrice () ) ) {
                    payable.setVerifyStatus ( "部分核销" );
                } else {
                    payable.setVerifyStatus ( "全部核销" );
                }
                payable.setVerifyAmount ( CommonUtil.doublePoint ( sum, 2 ) );
                //对应源单金额，如果当前应付单的源单是应付单，则为红冲，直接拿源单的《对应源单金额》字段取负数
                if ( payable.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_PAYABLE ) {
                    Optional<KingInvoicePayableEntity> first = kingInvoicePayableEntities.stream ().filter ( payableDetail -> payableDetail.getPayableDetailId ().equals ( payable.getPayableDetailId () ) ).findFirst ();
                    if ( first.isPresent () ) {
                        String sourceAmount = calSourceAmount ( first.get () );
                        payable.setSourceAmount ( CommonUtil.subStr ( "0", sourceAmount, 2 ) );
                    }
                } else {
                    String sourceAmount = calSourceAmount ( payable );
                    payable.setSourceAmount ( sourceAmount );
                }
                // 成本差异金额
                String differenceAmount = calCostDifferenceAmount ( payable );
                payable.setDifferenceAmount ( differenceAmount );
                // 对应源单价税合计
                if ( payable.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_PAYABLE ) {
                    Optional<KingInvoicePayableEntity> first = kingInvoicePayableEntities.stream ().filter ( payableDetail -> payableDetail.getPayableDetailId ().equals ( payable.getPayableDetailId () ) ).findFirst ();
                    if ( first.isPresent () ) {
                        String sourceTaxPriceAmount = calSourceTaxPriceAmount ( first.get () );
                        payable.setSourceTaxPriceAmount ( CommonUtil.subStr ( "0", sourceTaxPriceAmount, 2 ) );
                    }
                } else {
                    String sourceTaxPriceAmount = calSourceTaxPriceAmount ( payable );
                    payable.setSourceTaxPriceAmount ( sourceTaxPriceAmount );
                }
                // 价税合计差异金额，计算公式：（价税合计-对应源单价税合计）*汇率
                if ( "3".equals ( payable.getAccountType () ) ) {
                    String diff = CommonUtil.rideStr ( CommonUtil.subStr ( payable.getDetailTotalTaxPrice (), payable.getSourceTaxPriceAmount (), 2 ), payable.getRate (), 2 );
                    payable.setTaxPriceAmountDiff ( diff );
                }
                // 对应源单汇率
                String sourceRate = calSourceRate ( payable );
                payable.setSourceRate ( sourceRate );
                // 成本汇率差异金额，计算公式；对应源单金额*（汇率-源单汇率）
                String rateDiff = CommonUtil.rideStr ( CommonUtil.subStr ( payable.getRate (), payable.getSourceRate (), 2 ), payable.getSourceAmount (), 2 );
                payable.setRateDiff ( rateDiff );
                // 价税合计汇率差异金额，计算公式：对应源单价税合计*（汇率-源单汇率）
                String taxPriceAmountRateDiff = CommonUtil.rideStr ( payable.getSourceTaxPriceAmount (), CommonUtil.subStr ( payable.getRate (), payable.getSourceRate (), 2 ), 2 );
                payable.setTaxPriceAmountRateDiff ( taxPriceAmountRateDiff );
                //处理最后一个审批人
                ActTaskCommentEntity actTaskCommentEntity = iActTaskCommentService.queryLastComment ( payable.getInstanceId () );
                if ( actTaskCommentEntity != null ) {
                    payable.setApprovedUser ( actTaskCommentEntity.getApprovedUser () );
                    payable.setApprovedRemark ( actTaskCommentEntity.getRemark () );
                }
                //任务信息
                ActCurrentTaskVo currentTaskVo = iActModelService.getCurrentTaskByInstanceId ( payable.getInstanceId () );
                Optional.ofNullable ( currentTaskVo ).ifPresent ( currentTask -> {
                    payable.setCurrentTask ( currentTask );
                } );
            } );
        }
        return kingInvoicePayableEntities;
    }

    @Override
    public Integer queryCount ( KingInvoicePayableEntity kingInvoicePayableEntity ) {
        Integer integer = kingInvoicePayableEntityMapper.queryCount ( kingInvoicePayableEntity );
        return integer;
    }


    /**
     * 新增应付单（采购开票），有四种来源方式：
     * 1、手动新增，无上级单据
     * 2、采购订单新增
     * 3、采购入库单新增，要检查这个入库单已推了多少的应付单
     * 4、采购退料单
     * 5、应付单新增，冲红
     * 如果是选单方式，单据类型则必须是标准应付单
     * @param kingInvoicePayableDto 金蝶-发票-应付单/采购开票
     * @return 结果
     */
    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult draft ( KingInvoicePayableDto kingInvoicePayableDto ) {
        KingInvoicePayableEntity payableEntity = kingInvoicePayableDto.getKingInvoicePayableEntity ();
        payableEntity.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
        payableEntity.setDeptId ( CurrentUserUtil.getCompanyId () );
        payableEntity.setPayDeptId ( CurrentUserUtil.getCompanyId () );
        //生成编码=单据类型汉字
        String py = ChineseToPinyin.getFirstLetter ( "采购开票" );
        int count = this.count ( new QueryWrapper<> () );
        String countStr = String.format ( "%08d", count );
        payableEntity.setDocNo ( py.concat ( countStr ) );
        payableEntity.setSettlementDeptId ( CurrentUserUtil.getCompanyId () );
        payableEntity.setCreateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        payableEntity.setCreateDate ( DateUtils.getNowDate () );
        //物料明细行列表
        List<KingInvoicePayableDetailEntity> kingInvoicePayableDetailEntities = kingInvoicePayableDto.getKingInvoicePayableDetailEntities ();
        //2.采购订单下推：是否为采购订单入库，要检查是否已经入库
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_ORDER &&
                payableEntity.getIsTicketsBefore ().equals ( KingConstUtil.IS_TICKETS_BEFORE_YES ) ) {
            checkOrder ( kingInvoicePayableDto.getKingInvoicePayableDetailEntities () );
        }
        //3.采购入库单下推：要检查已推多少应付单，通过入库单查询已入库具体明细多少，只推剩余未入库的
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_IN_STORAGE ) {
            kingInvoicePayableDetailEntities = checkInventory ( kingInvoicePayableDto.getKingInvoicePayableDetailEntities () );
            double sum = kingInvoicePayableDetailEntities.stream ().mapToDouble ( payableDetail -> Double.parseDouble ( payableDetail.getTotalTaxPrice () ) ).sum ();
            payableEntity.setTotalTaxPrice ( String.valueOf ( sum ) );//明细价税合计求和给应付单总的价税合计
        }
        //4.采购退料单
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_REFUND ) {
            refund ( kingInvoicePayableDto.getKingInvoicePayableDetailEntities () );
        }
        //5.应付单冲红应付单：平掉，关联源应付单，并将原应付单的上级单据释放到可再次下推
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_PAYABLE ) {
            offset ( payableEntity );
        }
        //新增应付单明细
        int insertPayDetail = updatePayableDetail ( kingInvoicePayableDetailEntities, payableEntity.getId (), payableEntity.getSourceDocType () );
        if ( insertPayDetail == 0 ) {
            return AjaxResult.error ( "暂存采购开票明细失败！" );
        }
        //新增应付单付款计划
        List<KingInvoicePayableProcurePlanEntity> payableProcurePlanEntities = kingInvoicePayableDto.getKingInvoicePayableProcurePlanEntities ();
        int insertPlan = updatePayProcurePlan ( payableProcurePlanEntities, payableEntity.getId () );
        if ( insertPlan == 0 ) {
            return AjaxResult.error ( "暂存采购开票付款计划失败！" );
        }
        payableEntity.setState ( ConstUtil.STATE_IN_DRAFT );
        boolean save = this.save ( payableEntity );
        if ( !save ) {
            throw new CustomException ( "暂存采购开票基本信息失败！" );
        }
        return AjaxResult.success ( "暂存采购开票成功", payableEntity.getId () );
    }

    /**
     * 提交到审批状态
     * 1、新建审批流
     * 2、
     * @param payableEntity 金蝶-发票-应付单/采购开票
     * @return
     */
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult submit ( KingInvoicePayableEntity payableEntity ) {
        if ( StringUtils.isBlank ( payableEntity.getId () ) ) {
            return AjaxResult.error ("请先暂存该单据！");
        }
        StartProcessDto startDto = new StartProcessDto ().
                setBusinessId ( payableEntity.getId () ).
                setBusinessType ( payableEntity.getBusinessType () ).setModelId ( payableEntity.getModelId () ).
                setBusinessName ( "采购开票_".concat ( payableEntity.getId () ) );
        String instanceId = iActModelService.startProcess ( startDto );
        boolean save = this.update ( new LambdaUpdateWrapper<KingInvoicePayableEntity> ().
                set ( KingInvoicePayableEntity :: getState, ConstUtil.STATE_IN_PROCESS ).
                set ( KingInvoicePayableEntity :: getInstanceId, instanceId ).
                eq ( KingInvoicePayableEntity :: getId, payableEntity.getId () ) );
        if ( !save ) {
            throw new CustomException ( "提交采购开票失败！" );
        }
        return AjaxResult.success ( "提交采购开票成功" );
    }


    /**
     * 修改金蝶-发票-应付单/采购开票
     * @param kingInvoicePayableDto 金蝶-发票-应付单/采购开票
     * @return 结果
     */
    @Override
    public AjaxResult updateKingInvoicePayableEntity ( KingInvoicePayableDto kingInvoicePayableDto ) {
        KingInvoicePayableEntity payableEntity = kingInvoicePayableDto.getKingInvoicePayableEntity ();
        payableEntity.setUpdateDate ( DateUtils.getNowDate () );
        payableEntity.setUpdateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
        int i = kingInvoicePayableEntityMapper.updateKingInvoicePayableEntity ( payableEntity );
        if ( i == 0 ) {
            throw new CustomException ( "修改采购开票基本信息失败！" );
        }
        List<KingInvoicePayableDetailEntity> kingInvoicePayableDetailEntities = kingInvoicePayableDto.getKingInvoicePayableDetailEntities ();
        //2.采购订单下推：是否为采购订单入库，要检查是否已经入库
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_ORDER && payableEntity.getIsTicketsBefore ().equals ( KingConstUtil.IS_TICKETS_BEFORE_YES ) ) {
            checkOrder ( kingInvoicePayableDto.getKingInvoicePayableDetailEntities () );
        }
        //3.采购入库单下推：要检查已推多少应付单，通过入库单查询已入库具体明细多少，只推剩余未入库的
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_IN_STORAGE ) {
            kingInvoicePayableDetailEntities = checkInventory ( kingInvoicePayableDto.getKingInvoicePayableDetailEntities () );
            double sum = kingInvoicePayableDetailEntities.stream ().mapToDouble ( payableDetail -> Double.parseDouble ( payableDetail.getTotalTaxPrice () ) ).sum ();
            payableEntity.setTotalTaxPrice ( String.valueOf ( sum ) );//明细价税合计求和给应付单总的价税合计
        }
        //4.采购退料单
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_REFUND ) {
            refund ( kingInvoicePayableDto.getKingInvoicePayableDetailEntities () );
        }
        //5.应付单冲红应付单：平掉，关联源应付单，并将原应付单的上级单据释放到可再次下推
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_PAYABLE ) {
            offset ( payableEntity );
        }
        //新增应付单明细
        int insertPayDetail = updatePayableDetail ( kingInvoicePayableDetailEntities, payableEntity.getId (), payableEntity.getSourceDocType () );
        if ( insertPayDetail == 0 ) {
            return AjaxResult.error ( "新增采购开票明细失败！" );
        }
        //新增应付单付款计划
        List<KingInvoicePayableProcurePlanEntity> payableProcurePlanEntities = kingInvoicePayableDto.getKingInvoicePayableProcurePlanEntities ();
        int insertPlan = updatePayProcurePlan ( payableProcurePlanEntities, payableEntity.getId () );
        if ( insertPlan == 0 ) {
            return AjaxResult.error ( "新增采购开票付款计划失败！" );
        }
        return AjaxResult.success ();
    }

    /**
     * 批量删除金蝶-发票-应付单/采购开票
     * @param ids 需要删除的金蝶-发票-应付单/采购开票ID
     * @return 结果
     */
    @Override
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult deleteKingInvoicePayableEntityByIds (String[] ids) {
        KingInvoicePayableEntity payableEntity = new KingInvoicePayableEntity ();
        for ( int i = 0; i < ids.length; i++ ) {
            payableEntity.setId ( ids[0] );
            payableEntity.setIsUsed ( ConstUtil.IS_USED_NO );
            int update = kingInvoicePayableEntityMapper.updateKingInvoicePayableEntity ( payableEntity );
            if ( update == 0 ) {
                throw new CustomException ( "删除采购开票失败！" );
            }
        }
        LambdaUpdateWrapper<KingInvoicePayableDetailEntity> detailUpdate = new LambdaUpdateWrapper<> ();
        detailUpdate.set ( KingInvoicePayableDetailEntity :: getIsUsed, ConstUtil.IS_USED_NO ).in ( KingInvoicePayableDetailEntity :: getInvoicePayableId, ids );
        boolean updateDetail = iKingInvoicePayableDetailService.update ( detailUpdate );
        if ( ! updateDetail ) {
            throw new CustomException ( "删除采购开票失败！" );
        }
        return AjaxResult.success ( "删除采购开票成功" );
    }

    /**
     * 付款单选择应付单
     * @param kingInvoicePayableEntity
     * @return
     */
    @Override
    public List<KingInvoicePayableMenuVo> queryList ( KingInvoicePayableEntity kingInvoicePayableEntity ) {
        kingInvoicePayableEntity.setPage ( ( kingInvoicePayableEntity.getPage () - 1 ) * kingInvoicePayableEntity.getLimit () );
        List<KingInvoicePayableMenuVo> kingInvoicePayableMenuVos = kingInvoicePayableEntityMapper.queryList ( kingInvoicePayableEntity );
        //拿到所有的明细行，求数量之和
        List<String> ids = kingInvoicePayableMenuVos.stream ().map ( KingInvoicePayableMenuVo :: getSourceId ).collect ( Collectors.toList () );
        //所有单据的明细行
        if ( !CollectionUtils.isEmpty ( ids ) ) {
            List<KingInvoicePayableDetailEntity> list = iKingInvoicePayableDetailService.list ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().
                    in ( KingInvoicePayableDetailEntity :: getInvoicePayableId, ids ) );
            kingInvoicePayableMenuVos.forEach ( payable -> {
                // 求每个单据所有明细行的数量之和
                double sum = list.stream ().filter ( detail -> detail.getInvoicePayableId ().equals ( payable.getSourceId () ) ).mapToDouble ( detail -> Double.parseDouble ( StringUtils.isBlank ( detail.getNum () ) ? "0" : detail.getNum () ) ).sum ();
                payable.setNumber ( CommonUtil.doublePoint ( sum, 2 ) );
            } );
        }
        return kingInvoicePayableMenuVos;
    }

    @Override
    public Integer queryCountByMenu ( KingInvoicePayableEntity kingInvoicePayableEntity ) {
        Integer integer = kingInvoicePayableEntityMapper.queryCountByMenu ( kingInvoicePayableEntity );
        return integer;
    }

    /**
     * 应付单下推冲红应付单，以物料为主查询
     * @param kingInvoicePayableEntity
     * @return
     */
    @Override
    public AjaxResult queryListByPayable (KingInvoicePayableEntity kingInvoicePayableEntity) {
        kingInvoicePayableEntity.setPage ( ( kingInvoicePayableEntity.getPage () - 1 ) * kingInvoicePayableEntity.getLimit () );
        List<KingInvoicePayableMenuVo> kingInvoicePayableMenuVos = kingInvoicePayableEntityMapper.queryListByPayable ( kingInvoicePayableEntity );
        Integer count = kingInvoicePayableEntityMapper.queryCountByByPayable ( kingInvoicePayableEntity );
        PageParam pageParam = new PageParam ();
        pageParam.setRecords ( kingInvoicePayableMenuVos ).setTotal ( count );
        return AjaxResult.success ( pageParam );
    }

    /**
     * 通过采购订单直接下推应付单校验规则：
     * 1、最大下推数量=采购订单的剩余入库数量-采购订单提前开票数量
     * 2、剩余入库数量=采购订单物料数量-已入库数量(去入库单查)-退货补货数量(去采购退料单查，只查退货补货类型)
     * 3、提前开票数量=去应付单中查询该采购订单之前已经提前开票的数量
     * 故：最大下推数量=每个订单的每个物料数量-已经入库的数量-退货补货的数量-采购订单提前已经开票的数量
     * @param payableDetailEntities：应付单物料列表
     */
    private void checkOrder (List<KingInvoicePayableDetailEntity> payableDetailEntities) {
        //剔除没有源单id的明细行
        payableDetailEntities = payableDetailEntities.stream ().filter ( detail -> StringUtils.isNotBlank ( detail.getSourceMaterialDetailId () ) ).collect ( Collectors.toList () );
        //1.拿到物料明细的源单Id
        List<String> ids = payableDetailEntities.stream ().map ( detail -> detail.getSourceId () ).collect ( Collectors.toList () );
        //采购订单的物料明细
        List<MaterialDetailEntity> detailEntities = materialDetailMapper.selectList ( new QueryWrapper<MaterialDetailEntity> ().eq ( "type", ConstUtil.ORDER_PURCHASE ).in ( "relation_id", ids ) );
        log.info ( "已生成采购订单的物料列表：detailEntities->{}", detailEntities );
        for ( int i = 0; i < detailEntities.size (); i++ ) {
            for ( int j = 0; j < payableDetailEntities.size (); j++ ) {
                if ( detailEntities.get ( i ).getId ().equals ( payableDetailEntities.get ( j ).getSourceMaterialDetailId () ) ) {
                    //新增界面的物料数量
                    double numByPayable = Double.parseDouble ( payableDetailEntities.get ( j ).getNum () );
                    //订单中的每个物料数量
                    String numByOrder = detailEntities.get ( i ).getNumber ();
                    //该物料已入库的数量
                    List<MaterialDetailEntity> listByMaterial = materialDetailMapper.selectList ( new QueryWrapper<MaterialDetailEntity> ().
                            eq ( "material_id", detailEntities.get ( i ).getId () ).
                            eq ( "type", ConstUtil.ORDER_IN_STORAGE ).
                            eq ( "relation_id", detailEntities.get ( i ).getRelationId () ) );
                    double numByIn = listByMaterial.stream ().mapToDouble ( material -> Double.parseDouble ( material.getNumber () ) ).sum ();
                    //退货补货的数量，负数
                    KingProcureRefundMaterialEntity procureRefundMaterialEntity = new KingProcureRefundMaterialEntity ();
                    procureRefundMaterialEntity.setSourceOrderId ( detailEntities.get ( i ).getRelationId () ).
                            setMaterialId ( detailEntities.get ( i ).getId () ).
                            setType ( KingConstUtil.PROCURE_REFUND_TYPE_REFUND );
                    List<KingProcureRefundMaterialEntity> kingProcureRefundMaterialEntities = iKingProcureRefundMaterialService.queryRelationList ( procureRefundMaterialEntity );
                    double numByRefund = kingProcureRefundMaterialEntities.stream ().mapToDouble ( x -> Double.parseDouble ( x.getRefundNum () ) ).sum ();
                    //已提前下推开票数量，通过采购订单查询
                    List<KingInvoicePayableDetailEntity> listByPayable = iKingInvoicePayableDetailService.list ( new QueryWrapper<KingInvoicePayableDetailEntity> ().
                            eq ( "material_id", detailEntities.get ( i ).getId () ).
                            eq ( "order_id", detailEntities.get ( i ).getRelationId () ).
                            eq ( "is_used", ConstUtil.IS_USED_YES ) );
                    double count = listByPayable.stream ().mapToDouble ( x -> Double.parseDouble ( x.getNum () ) ).sum ();
                    if ( numByPayable > ( Double.parseDouble ( numByOrder ) - numByIn - numByRefund - count ) ) {
                        throw new RuntimeException ( "通过采购订单下推应付单，第【" + payableDetailEntities.get ( i ).getLineNumber () + "】分录行数量超限！" );
                    }
                }
            }
        }
    }

    /**
     * 通过采购入库单下推应付单校验规则：
     * 1、最大下推数量=剩余未开票数量
     * 2、剩余未开票数量=累计入库数量-累计开票数量
     * 3、累计入库数量=去入库单中查
     * 4、累计开票数量=去应付单中查
     * 5、检查状态为创建的占用应付数量，要减去
     * 故：最大下推数量=累计入库数量(去入库单中查)-累计开票数量(去应付单中查)
     * @param payableDetailEntities
     */
    private List<KingInvoicePayableDetailEntity> checkInventory (List<KingInvoicePayableDetailEntity> payableDetailEntities) {
        payableDetailEntities = payableDetailEntities.stream ().filter ( detail -> StringUtils.isNotBlank ( detail.getSourceId () ) ).collect ( Collectors.toList () );
        //1.拿到入库单明细行物料Id
        List<String> materialIds = payableDetailEntities.stream ().map ( KingInvoicePayableDetailEntity :: getSourceMaterialDetailId ).collect ( Collectors.toList () );
        //2.拿到选中的入库单的所有入库明细
        List<KingInventoryMaterialEntity> inventoryMaterialEntities = iKingInventoryMaterialService.list ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().in ( KingInventoryMaterialEntity :: getId, materialIds ) );
        log.info ( "已入库的物料明细列表：detailEntities->{}", inventoryMaterialEntities );
        payableDetailEntities.forEach ( payableDetail -> {
            //新增界面的物料数量
            double addNum = StringUtils.isBlank ( payableDetail.getNum () ) ? 0.0 : Double.parseDouble ( payableDetail.getNum () );
            //源单物料明细行Id=源单明细行主键
            String sourceMaterialDetailId = payableDetail.getSourceMaterialDetailId ();
            if ( StringUtils.isNotBlank ( sourceMaterialDetailId ) ) {
                //已下推物料数量
                double executeSum = 0;
                KingInvoicePayableDetailEntity one = iKingInvoicePayableDetailService.getOne ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().
                        eq ( KingInvoicePayableDetailEntity :: getSourceMaterialDetailId, sourceMaterialDetailId ).
                        eq ( KingInvoicePayableDetailEntity :: getIsUsed, ConstUtil.IS_USED_YES ).
                        eq ( KingInvoicePayableDetailEntity :: getIsRelease, false ) );
                if ( one != null ) {
                    executeSum = Double.parseDouble ( one.getNum () );
                }
                //入库物料总数量
                double totalNum = inventoryMaterialEntities.stream ().filter ( material -> material.getId ().equals ( sourceMaterialDetailId ) ).mapToDouble ( material -> Double.parseDouble ( StringUtils.isBlank ( material.getReceiptsNum () ) ? "0" : material.getReceiptsNum () ) ).sum ();
                if ( addNum > ( totalNum - executeSum ) ) {
                    throw new CustomException ( "通过采购入库单下推应付单，物料数量超限！" );
                }
                //平账，如果总的数量 减去 已下推 正好等于本次新增的数量，表示推完了
                if ( addNum == ( totalNum - executeSum ) ) {
                    // 拿到入库单明细行的金额信息
                    KingInventoryMaterialEntity inventoryMaterialEntity = iKingInventoryMaterialService.getOne ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
                            eq ( KingInventoryMaterialEntity :: getId, sourceMaterialDetailId ) );
                    String totalTaxPrice = inventoryMaterialEntity.getTotalTaxPrice ();// 价税合计
                    String price = inventoryMaterialEntity.getPrice (); // 单价
                    String amountMoney = inventoryMaterialEntity.getMoneyAmount ();//不含税金额
                    payableDetail.setTotalTaxPrice ( totalTaxPrice );//平价税合计
                    payableDetail.setPrice ( price );//平含税单价
                    payableDetail.setNotTaxAmount ( amountMoney );
                }
            }
        } );
//        for ( int i = 0; i < inventoryMaterialEntities.size (); i++ ) {
//            for ( int j = 0; j < payableDetailEntities.size (); j++ ) {
//                //同一个物料
//                if ( inventoryMaterialEntities.get ( i ).getId ().equals ( payableDetailEntities.get ( j ).getMaterialCode () ) ) {
//                    //新增界面本次的物料数量
//                    double numByPayable = Double.parseDouble ( payableDetailEntities.get ( j ).getNum () );
//                    //入库物料数量
//                    double numByOrder = Double.parseDouble ( inventoryMaterialEntities.get ( i ).getReceiptsNum () );
//                    //已下推生成应付单的物料数量
//                    List<KingInvoicePayableDetailEntity> list = iKingInvoicePayableDetailService.list ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().
//                            eq ( KingInvoicePayableDetailEntity :: getSourceMaterialDetailId, inventoryMaterialEntities.get ( i ).getId () ).
//                            eq ( KingInvoicePayableDetailEntity :: getSourceId, inventoryMaterialEntities.get ( i ).getRelationId () ).
//                            eq ( KingInvoicePayableDetailEntity :: getIsUsed, ConstUtil.IS_USED_YES ) );
//                    double numByPayed = list.stream ().mapToDouble ( x -> Double.parseDouble ( x.getNum () ) ).sum ();
//
//                }
//            }
//        }
        return payableDetailEntities;
    }

    /**
     * 采购退料单下推应付单校验规则：
     * 检查每个退料单的每个物料的数量，实际下推数量不能大于物料数量
     * @param payableDetailEntities：应付单新增界面物料列表
     */
    private void refund (List<KingInvoicePayableDetailEntity> payableDetailEntities) {
        //采购退料单ids
        List<String> ids = payableDetailEntities.stream ().map ( KingInvoicePayableDetailEntity :: getSourceId ).collect ( Collectors.toList () );
        List<KingProcureRefundMaterialEntity> list = iKingProcureRefundMaterialService.list ( new QueryWrapper<KingProcureRefundMaterialEntity> ().in ( "relation_id", ids ) );
        for ( int i = 0; i < list.size (); i++ ) {
            for ( int j = 0; j < payableDetailEntities.size (); j++ ) {
                double numByPayable = Double.parseDouble ( payableDetailEntities.get ( j ).getNum () );
                if ( Math.abs ( numByPayable ) > Math.abs ( Double.parseDouble ( list.get ( i ).getRefundNum () ) ) ) {
                    throw new CustomException ( "通过采购退料单下推应付单，第【" + payableDetailEntities.get ( j ).getLineNumber () + "】分录行数量超限！" );
                }
            }
        }
    }

    /**
     * 冲红下推应付单，平掉源应付单，并且删除下推生成源应付单的上级关系
     * 1、源应付单是上级采购订单下推生成
     * 2、源应付单是上级采购入库单下推生成
     * @param kingInvoicePayableEntity
     */
    private void offset (KingInvoicePayableEntity kingInvoicePayableEntity) {
        if ( StringUtils.isBlank ( kingInvoicePayableEntity.getParentId () ) ) {
            throw new RuntimeException ( "上级源应付单不能为空" );
        }
        KingInvoicePayableEntity parentEntity = this.getById ( kingInvoicePayableEntity.getParentId () );
        if ( parentEntity == null ) {
            throw new CustomException ( "所选单据不存在" );
        }
        //全部红冲
        double sum = Double.sum ( Double.parseDouble ( StringUtils.isBlank ( parentEntity.getTotalTaxPrice () ) ? "0" : parentEntity.getTotalTaxPrice () ), Double.parseDouble ( StringUtils.isBlank ( kingInvoicePayableEntity.getTotalTaxPrice () ) ? "0" : kingInvoicePayableEntity.getTotalTaxPrice () ) );
        if ( sum != 0 ) {
            throw new CustomException ( "冲红单据的金额必须为负数" );
        }
        if ( ! parentEntity.getSupplierId ().equals ( kingInvoicePayableEntity.getSupplierId () ) ) {
            throw new CustomException ( "冲红单据的供应商必须与源单据供应商保持一致" );
        }
        if ( ! parentEntity.getSettlementDeptId ().equals ( kingInvoicePayableEntity.getSettlementDeptId () ) ) {
            throw new CustomException ( "冲红单据的结算组织必须与源单据结算组织保持一致" );
        }
        //删除源应付单的上级单据的下推记录
        log.info ( "冲红成功，父单据【{}】！", parentEntity.getDocNo () );
        //更新源应付单状态为：冲红作废
        //boolean update = this.update ( new UpdateWrapper<KingInvoicePayableEntity> ().
        //        set ( "status", KingConstUtil.KING_DATA_EXAMINE_STATUS_OFFSET ).
        //        set ( "change_time", DateUtils.getNowDate () ).
        //        eq ( "id", parentEntity.getId () ) );
        //if ( ! update ) {
        //    throw new RuntimeException ( "修改父应付单【" + parentEntity.getDocNo () + "】冲红失败" );
        //}
        //更新应付明细作废
        //boolean updateDetail = iKingInvoicePayableDetailService.update ( new UpdateWrapper<KingInvoicePayableDetailEntity> ().
        //        set ( "change_time", DateUtils.getNowDate () ).
        //        set ( "is_used", ConstUtil.IS_USED_NO ).
        //        eq ( "invoice_payable_id", parentEntity.getId () ) );
        //if ( ! updateDetail ) {
        //    throw new RuntimeException ( "更新应付明细作废失败" );
        //}
    }


    /**
     * 应付单审批完成后业务处理：
     * 1、应付单审核完要做的事情一：生成差异日志，首先检查是不是暂估应付单
     * 	1）如果是暂估应付单，必须生成差异日志；
     * 	2）如果是财务应付单，看成本差异金额，如果差异日志那一列有值就要生成差异日志；
     *  3）然后差异日志记录里同一个序号的，业务日期不在同一个月份的才要生成成本调整单；差异日志记录里同一个序号的，业务日期在同一个月份的本次钩稽金额需要回写入入库单的当月物料成本，本次钩稽金额本位币需要回写入入库单的当月物料成本（本位币）中；
     * 	4）生成成本调整单的时候 检查同物料同批号在本月以前有没有出库，如果有按出库数量除以总数量的比例生成出库成本调整单；
     * 2、应付单审核完要做的事情二：如果是红冲的应付单，要处理红冲状态；
     *  1）入库单 -》 应付单 -》 红冲应付单
     *  这个冲红审核后，等于是把应付单和冲红应付单互怼，此时相当于入库单没推，则释放入库单，后续还可以继续下推应付单
     * 3、修改审批流状态
     * 4、推送金蝶
     * @param id
     * @return
     */
    @Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public AjaxResult approve ( String id ) {
        KingInvoicePayableEntity payableEntity = querySingleById ( id );
        if ( payableEntity == null ) {
            throw new CustomException ( "该单据不存在！" );
        }
        //1.通过应付单id获取应付单明细，有源单id和源单物料明细id（入库单下推的应付单明细）
        List<KingInvoicePayableDetailEntity> payableDetailEntitiesByInventory = iKingInvoicePayableDetailService.list ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().
                eq ( KingInvoicePayableDetailEntity :: getInvoicePayableId, id ) );
        /*if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_IN_STORAGE ) {
            if ( ! CollectionUtils.isEmpty ( payableDetailEntitiesByInventory ) ) {
                //2.通过应付单明细获取采购订单Ids
                List<String> orderIds = payableDetailEntitiesByInventory.stream ().distinct ().map ( KingInvoicePayableDetailEntity :: getOrderId ).collect ( Collectors.toList () );
                if ( ! CollectionUtils.isEmpty ( orderIds ) ) {
                    //3.通过采购订单Id获取应付单，表示之前有推过，如果没有则啥也不用管
                    List<KingInvoicePayableDetailEntity> payableDetailEntitiesByOrder = iKingInvoicePayableDetailService.list ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().
                            in ( KingInvoicePayableDetailEntity :: getSourceId, orderIds ) );
                    //4.根据物料分组，求和，看是否冲完
                    Map<String, List<KingInvoicePayableDetailEntity>> map = payableDetailEntitiesByOrder.stream ().collect ( Collectors.groupingBy ( KingInvoicePayableDetailEntity :: getMaterialCode ) );
                    for ( Map.Entry<String, List<KingInvoicePayableDetailEntity>> entry : map.entrySet () ) {
                        List<KingInvoicePayableDetailEntity> value = entry.getValue ();//应付单明细一正一负集合
                        //5.求一正一负互怼之后是否有结余
                        double num = value.stream ().mapToDouble ( detail -> Double.parseDouble ( detail.getNum () ) ).sum (); //数量
                        double totalTaxPrice = value.stream ().mapToDouble ( detail -> Double.parseDouble ( detail.getTotalTaxPrice () ) ).sum ();//价税合计
                        double rateAmount = value.stream ().mapToDouble ( detail -> Double.parseDouble ( detail.getRateAmount () ) ).sum ();//税额
                        double amount = value.stream ().mapToDouble ( detail -> Double.parseDouble ( detail.getNotTaxAmount () ) ).sum ();//金额
                        //6.拿到该审批完成应付单的源单（入库单）的数量
                        KingInventoryMaterialEntity one = iKingInventoryMaterialService.getOne ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
                                eq ( KingInventoryMaterialEntity :: getRelationId, value.get ( 0 ).getSourceId () ).
                                eq ( KingInventoryMaterialEntity :: getSourceMaterialDetailId, value.get ( 0 ).getSourceMaterialDetailId () ) );
                        double receiptsNum = 0;
                        if ( one != null ) {
                            receiptsNum = Double.parseDouble ( one.getReceiptsNum () );
                        }
                        if ( num != 0 ) {
                            //表示还未冲完
                            KingInvoicePayableEntity payable = new KingInvoicePayableEntity ();
                            BeanUtils.copyProperties ( payableEntity, payable );
                            payable.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
                            payable.setParentId ( id );// 父应付单红冲
                            //编码拿父应付单的编码二次编码
                            int count = this.count ( new LambdaQueryWrapper<KingInvoicePayableEntity> ().eq ( KingInvoicePayableEntity :: getParentId, id ) );
                            String format = String.format ( "%3", count );
                            payable.setDocNo ( payableEntity.getDocNo ().concat ( "_" ).concat ( format ) );
                            payable.setBizTime ( DateUtils.getNowDate () );
                            if ( num - receiptsNum == 0 ) {
                                //最后一次，平账

                            }
                            //计算数量和金额相关
                            payable.setCreateUserId ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
                            payable.setCreateDate ( DateUtils.getNowDate () );
                            int i = kingInvoicePayableEntityMapper.insertKingInvoicePayableEntity ( payable );
                            if ( i == 0 ) {
                                throw new CustomException ( "自动红冲失败！" );
                            }
                            //7.红冲明细，所有物料
                            double finalReceiptsNum = receiptsNum;
                            payableDetailEntitiesByInventory.forEach ( detail -> {
                                detail.setId ( SnowflakeIdWorker.getInstance ().nextStringId () );
                                if ( num - finalReceiptsNum == 0 ) {
                                    //最后一次，需要平账

                                }
                                detail.setCreateBy ( SecurityUtils.getLoginUser ().getUser ().getUserId () );
                                detail.setCreateTime ( DateUtils.getNowDate () );
                                detail.setSourceDocType ( KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_ORDER );
                                detail.setSourceId ( value.get ( 0 ).getSourceId () );
                                detail.setOrderCode ( value.get ( 0 ).getOrderCode () );
                                boolean save = iKingInvoicePayableDetailService.save ( detail );
                                if ( ! save ) {
                                    throw new CustomException ( "自动红冲明细失败！" );
                                }
                            } );
                        }
                    }
                }
            }
        }*/
        //1.生成差异日志和成本调整单
        payableDetailEntitiesByInventory.forEach ( payableDetail -> {
            if ( KingConstUtil.ACCOUNT_TYPE_TEM.equals ( payableEntity.getAccountType () ) ) {
                // 暂估应付(费用应付单)生成差异日志
                saveDiffLogByFee ( payableEntity, payableDetail );
                log.info ( "暂估应付单生成差异日志成功。" );
            } else if ( KingConstUtil.ACCOUNT_TYPE_MEET.equals ( payableEntity.getAccountType () ) ) {
                // 如果是财务应付，那么应付单成本差异金额不为0的则生成差异日志
                String differenceAmount = costDifferenceAmount ( payableEntity, payableDetail );
                if ( StringUtils.isNotBlank ( differenceAmount ) ) {
                    saveDiffLog ( payableEntity, payableDetail, differenceAmount );
                    log.info ( "财务应付单生成差异日志成功。" );
                }
            }
        } );
        //2.如果要红冲的应付单，怼掉红冲前记录
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_PAYABLE ) {
            List<KingInvoicePayableDetailEntity> list = iKingInvoicePayableDetailService.list ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().
                    eq ( KingInvoicePayableDetailEntity :: getInvoicePayableId, payableEntity.getId () ) );
            //源应付单明细行的id，设置为释放
            List<String> sourcePayableDetailIds = list.stream ().map ( KingInvoicePayableDetailEntity :: getSourceMaterialDetailId ).collect ( Collectors.toList () );
            sourcePayableDetailIds.forEach ( sourceDetail -> {
                iKingInvoicePayableDetailService.update ( new LambdaUpdateWrapper<KingInvoicePayableDetailEntity> ().set ( KingInvoicePayableDetailEntity :: getIsRelease, true ).eq ( KingInvoicePayableDetailEntity :: getId, sourceDetail ) );
                log.info ( "红冲释放完成。" );
            } );
        }
        //3.修改审批流状态
        boolean update = this.update ( new LambdaUpdateWrapper<KingInvoicePayableEntity> ().set ( KingInvoicePayableEntity :: getState, ConstUtil.STATE_FINISH ).eq ( KingInvoicePayableEntity :: getId, id ) );
        if ( ! update ) {
            throw new CustomException ( "修改应付单审批流失败！" );
        }
        log.info ( "应付单审批流状态修改成功。" );
        //4.推送金蝶
        //payablePushService.push(payableEntity);
        //log.info ( "应付单推送金蝶成功。" );
        return AjaxResult.success ("采购开票审核并推送金蝶成功");
    }

    /**
     * 入库单审核完成后调用该方法，检查该入库单的源单采购订单之前是否已推应付单
     * 如果已推应付单，则生成一正一负应付单，源单分别为入库单（正）和采购订单（负）
     *
     * @param inventoryId：入库单的Id
     * @param orderId：采购订单Id
     * @return
     */
    public AjaxResult inventoryApprove ( String inventoryId, String orderId ) {
        KingInvoicePayableDto dtoByInventory = new KingInvoicePayableDto ();
        KingInventoryEntity inventoryEntity = iKingInventoryService.getById ( inventoryId );
        if ( inventoryEntity == null ) {
            return AjaxResult.error ( "无此入库单" );
        }
        List<KingInvoicePayableDetailEntity> list = iKingInvoicePayableDetailService.list ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().
                eq ( KingInvoicePayableDetailEntity :: getSourceId, orderId ) );
        if ( list == null || list.size () <= 0 ) {
            return AjaxResult.error ( "未提前生成应付单" );
        }
        List<KingInventoryMaterialEntity> inventoryMaterialEntities = iKingInventoryMaterialService.list ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
                eq ( KingInventoryMaterialEntity :: getInventoryId, inventoryId ) );
        //生成源单为入库单的正应付单
        KingInvoicePayableEntity payableEntityByInventory = new KingInvoicePayableEntity ();
        payableEntityByInventory.setDocTypeCode ( KingConstUtil.KING_PAYABLE_STAND );
        payableEntityByInventory.setBizTime ( DateUtils.getNowDate () );
        payableEntityByInventory.setDueTime ( DateUtils.getNowDate () );
        payableEntityByInventory.setSupplierId ( inventoryEntity.getSupplierId () );
        KingBaseCurrencyEntity currencyEntity = iKingBaseCurrencyService.getOne ( new LambdaQueryWrapper<KingBaseCurrencyEntity> ().eq ( KingBaseCurrencyEntity :: getCode, inventoryEntity.getSettlementCurrency () ) );
        payableEntityByInventory.setCurrencyId ( currencyEntity.getId () );
        payableEntityByInventory.setTotalTaxPrice ( inventoryEntity.getTotalPriceTax () );
        payableEntityByInventory.setSourceDocType ( 3 );
        payableEntityByInventory.setRemarks ( "系统自动生成蓝冲单" );
        SysDept one = iSysDeptService.getOne ( new LambdaQueryWrapper<SysDept> ().eq ( SysDept :: getDeptCode, inventoryEntity.getPurchaseDept () ) );
        if ( one != null ) {
            payableEntityByInventory.setProcureDeptId ( one.getDeptId () );
        }
        SysUser user = iSysUserService.getOne ( new LambdaQueryWrapper<SysUser> ().eq ( SysUser :: getUserCode, inventoryEntity.getPurchaseBy () ) );
        if ( user != null ) {
            payableEntityByInventory.setProcureUserId ( user.getUserId () );
        }
        SysDept purchase = iSysDeptService.getOne ( new LambdaQueryWrapper<SysDept> ().eq ( SysDept :: getDeptCode, inventoryEntity.getPurchaseOrg () ) );
        if ( purchase != null ) {
            payableEntityByInventory.setProcureCompanyId ( purchase.getDeptId () );
            payableEntityByInventory.setPayDeptId ( purchase.getDeptId () );
            payableEntityByInventory.setSettlementDeptId ( purchase.getDeptId () );
        }
        payableEntityByInventory.setAccountType ( "3" );
        payableEntityByInventory.setDueCalculationTime ( DateUtils.getNowDate () );
        payableEntityByInventory.setOrderSupplierId ( inventoryEntity.getSupplierId () );
        payableEntityByInventory.setGoodsSupplierId ( inventoryEntity.getSupplierId () );
        payableEntityByInventory.setReceiptSupplierId ( inventoryEntity.getSupplierId () );
        payableEntityByInventory.setIsExtraPriceTax ( true );
        payableEntityByInventory.setIsIncludeTaxPrice ( true );
        KingBaseSettlementTypeEntity settMethod = iKingBaseSettlementTypeService.getOne ( new LambdaQueryWrapper<KingBaseSettlementTypeEntity> ().eq ( KingBaseSettlementTypeEntity :: getBizCode, inventoryEntity.getSettlementMethod () ) );
        payableEntityByInventory.setSettlementTypeId ( settMethod.getId () );
        payableEntityByInventory.setRate ( inventoryEntity.getExchangeRate () );
        payableEntityByInventory.setRateAmount ( inventoryEntity.getTaxAmount () );
        payableEntityByInventory.setNotTaxAmount ( inventoryEntity.getMoneyAmount () );
        payableEntityByInventory.setTotalTaxPriceCurrency ( CommonUtil.rideStr ( inventoryEntity.getExchangeRate (), inventoryEntity.getTotalPriceTax (), 2 ) );
        payableEntityByInventory.setRateAmountCurrency ( CommonUtil.rideStr ( inventoryEntity.getExchangeRate (), inventoryEntity.getTaxAmount (), 2 ) );
        payableEntityByInventory.setNotTaxAmountCurrency ( CommonUtil.rideStr ( inventoryEntity.getExchangeRate (), inventoryEntity.getMoneyAmount (), 2 ) );
        List<KingInvoicePayableDetailEntity> payableDetailEntitiesByInventory = new ArrayList<> ();
        inventoryMaterialEntities.forEach ( material -> {
            KingInvoicePayableDetailEntity payableDetailEntity = new KingInvoicePayableDetailEntity ();
            payableDetailEntity.setMaterialId ( material.getMaterialId () );
            payableDetailEntity.setMaterialCode ( material.getMaterialCode () );
            payableDetailEntity.setUnit ( material.getPricingUnit () );
            payableDetailEntity.setPrice ( material.getPrice () ); //业务单价
            payableDetailEntity.setNum ( material.getReceiptsNum () ); //实收数量
            payableDetailEntity.setTaxPrice ( material.getBusinessTaxPrice () );//含税单价
            payableDetailEntity.setRate ( material.getTaxRate () );
            payableDetailEntity.setNotTaxAmount ( material.getMoneyAmount () );
            payableDetailEntity.setRateAmount ( material.getTaxAmount () );
            payableDetailEntity.setTotalTaxPrice ( material.getTotalTaxPrice () );
            payableDetailEntity.setBaseUnit ( material.getPricingNum () );
            payableDetailEntity.setBatchNum ( material.getBatchNum () );
            payableDetailEntity.setInventoryNum ( material.getPricingNum () );
            payableDetailEntity.setSourceDocType ( 3 );//入库单
            payableDetailEntity.setSourceId ( inventoryId );
            payableDetailEntity.setSourceMaterialDetailId ( material.getId () );
            payableDetailEntity.setRemarks ( "系统自动生成蓝冲单" );
            payableDetailEntitiesByInventory.add ( payableDetailEntity );
        } );
        dtoByInventory.setKingInvoicePayableEntity ( payableEntityByInventory );
        dtoByInventory.setKingInvoicePayableDetailEntities ( payableDetailEntitiesByInventory );
        this.draft ( dtoByInventory );
        //生成负的（红冲）应付单，源单为采购订单
        KingInvoicePayableDto dtoByOrder = new KingInvoicePayableDto ();
        OrderEntity orderEntity = iOrderService.getById ( orderId );
        if ( orderEntity == null ) {
            return AjaxResult.error ( "无此采购订单" );
        }
        List<MaterialDetailEntity> materialDetailEntities = iMaterialDetailService.list ( new LambdaQueryWrapper<MaterialDetailEntity> ().
                eq ( MaterialDetailEntity :: getRelationId, orderId ).eq ( MaterialDetailEntity :: getType, ConstUtil.ORDER_PURCHASE ) );
        KingInvoicePayableEntity payableEntityByOrder = new KingInvoicePayableEntity ();
        payableEntityByOrder.setDocTypeCode ( KingConstUtil.KING_PAYABLE_STAND );
        payableEntityByOrder.setBizTime ( DateUtils.getNowDate () );
        payableEntityByOrder.setDueTime ( DateUtils.getNowDate () );
        payableEntityByOrder.setSupplierId ( orderEntity.getSupplierId () );
        payableEntityByOrder.setCurrencyId ( orderEntity.getSettlementCurrency () );
        payableEntityByOrder.setTotalTaxPrice ( orderEntity.getTotalPriceTax () );
        payableEntityByOrder.setRemarks ( "系统自动生成红冲单" );
        one = iSysDeptService.getOne ( new LambdaQueryWrapper<SysDept> ().eq ( SysDept :: getDeptCode, orderEntity.getPurchaseDept () ) );
        if ( one != null ) {
            payableEntityByOrder.setProcureDeptId ( one.getDeptId () );
        }
        user = iSysUserService.getOne ( new LambdaQueryWrapper<SysUser> ().eq ( SysUser :: getUserCode, orderEntity.getApplyId () ) );
        if ( user != null ) {
            payableEntityByOrder.setProcureUserId ( user.getUserId () );
        }
        payableEntityByOrder.setProcureCompanyId ( orderEntity.getPurchaseOrg () );
        payableEntityByOrder.setPayDeptId ( orderEntity.getPurchaseOrg () );
        payableEntityByOrder.setSettlementDeptId ( orderEntity.getPurchaseOrg () );
        payableEntityByOrder.setAccountType ( "3" );
        payableEntityByOrder.setDueCalculationTime ( DateUtils.getNowDate () );
        payableEntityByOrder.setOrderSupplierId ( orderEntity.getSupplierId () );
        payableEntityByOrder.setGoodsSupplierId ( orderEntity.getSupplierId () );
        payableEntityByOrder.setReceiptSupplierId ( orderEntity.getSupplierId () );
        payableEntityByOrder.setIsExtraPriceTax ( true );
        payableEntityByOrder.setIsIncludeTaxPrice ( true );
        settMethod = iKingBaseSettlementTypeService.getOne ( new LambdaQueryWrapper<KingBaseSettlementTypeEntity> ().eq ( KingBaseSettlementTypeEntity :: getBizCode, orderEntity.getSettlementMethod () ) );
        payableEntityByOrder.setSettlementTypeId ( settMethod.getId () );
        payableEntityByOrder.setRate ( orderEntity.getExchangeRate () );
        payableEntityByOrder.setRateAmount ( orderEntity.getTaxAmount () );
        payableEntityByOrder.setNotTaxAmount ( orderEntity.getMoneyAmount () );
        payableEntityByOrder.setTotalTaxPriceCurrency ( CommonUtil.rideStr ( orderEntity.getExchangeRate (), orderEntity.getTotalPriceTax (), 2 ) );
        payableEntityByOrder.setRateAmountCurrency ( CommonUtil.rideStr ( orderEntity.getExchangeRate (), orderEntity.getTaxAmount (), 2 ) );
        payableEntityByOrder.setNotTaxAmountCurrency ( CommonUtil.rideStr ( orderEntity.getExchangeRate (), orderEntity.getMoneyAmount (), 2 ) );
        List<KingInvoicePayableDetailEntity> payableDetailEntitiesByOrder = new ArrayList<> ();
        materialDetailEntities.forEach ( detail -> {
            KingInvoicePayableDetailEntity payableDetailEntity = new KingInvoicePayableDetailEntity ();
            payableDetailEntity.setMaterialId ( detail.getMaterialId () );
            payableDetailEntity.setMaterialCode ( detail.getMaterialCode () );
            payableDetailEntity.setUnit ( detail.getPricingUnit () );
            payableDetailEntity.setPrice ( detail.getPrice () ); //单价
            payableDetailEntity.setNum ( detail.getNumber () ); //数量
            payableDetailEntity.setTaxPrice ( detail.getBusinessTaxPrice () );//含税单价
            payableDetailEntity.setRate ( detail.getTaxRate () );
            payableDetailEntity.setNotTaxAmount ( detail.getNoTaxMoney () );
            payableDetailEntity.setRateAmount ( detail.getTaxAmount () );
            payableDetailEntity.setTotalTaxPrice ( detail.getTotalTaxPrice () );
            payableDetailEntity.setBaseUnit ( detail.getPricingNum () );
            payableDetailEntity.setBatchNum ( detail.getBatchNum () );
            payableDetailEntity.setInventoryNum ( String.valueOf ( detail.getInventoryNum () ) );
            payableDetailEntity.setSourceDocType ( 2 );//入库单
            payableDetailEntity.setSourceId ( orderId );
            payableDetailEntity.setSourceMaterialDetailId ( detail.getId () );
            payableDetailEntitiesByOrder.add ( payableDetailEntity );
        } );
        dtoByOrder.setKingInvoicePayableEntity ( payableEntityByOrder );
        dtoByOrder.setKingInvoicePayableDetailEntities ( payableDetailEntitiesByOrder );
        this.draft ( dtoByOrder );
        return AjaxResult.success ( "入库单审核后自动生成应付单成功" );
    }

    /**
     * 通过暂估（费用）应付单生成差异日志，注意：
     * 1、费用应付单关联入库单时(实际是关联入库单明细)，生成差异日志时是多条对多条
     * 2、勾稽金额分几种情况：
     * 1）如果费用应付单的源单类型为空：
     *      看该费用应付单的《税额计入成本》是否为true，则取该费用应付单的《税额+金额》
     *      看该费用应付单的《税额计入成本》是否为false，则取该费用应付单的《金额》
     * 2）如果费用应付单的源单类型不为空且为费用应付单，则取该费用应付单的《成本差异金额》
     * @param payableEntity
     * @param payableDetailEntity
     */
    private void saveDiffLogByFee (KingInvoicePayableEntity payableEntity, KingInvoicePayableDetailEntity payableDetailEntity) {
        //1.生成《费用应付单》的差异日志
        KingDiffLogEntity logEntity = new KingDiffLogEntity ();
        //求勾稽序号
        String serialNumber = String.format ( "%08d", 0 );
        KingDiffLogEntity one = iKingDiffLogService.getOne ( new LambdaQueryWrapper<KingDiffLogEntity> ().select ( KingDiffLogEntity :: getSerialNumber ).orderByDesc ( KingDiffLogEntity :: getSerialNumber ).last ( "LIMIT 1" ) );
        if ( one != null ) {
            serialNumber = one.getSerialNumber ();
        }
        SysDept deptEntity = iSysDeptService.getById ( CurrentUserUtil.getCompanyId () );
        logEntity.setStandardCurrencyCode ( deptEntity.getStandardCurrencyCode () ).setSerialNumber ( String.format ( "%08d", ( Integer.parseInt ( serialNumber ) + 1 ) ) ).
                setDocNo ( payableEntity.getDocNo () ).setLineNumber ( payableDetailEntity.getLineNumber () ).setAllocationCriteria ( KingConstUtil.AMOUNT_CURRENCY ).
                setDocTypeCode ( payableEntity.getDocTypeCode () ).setBizTime ( payableEntity.getBizTime () ).setMaterialId ( payableDetailEntity.getMaterialId () ).
                setSourceMaterialDetailId ( payableDetailEntity.getId () );
        //求勾稽金额
        String verifyAmount = "0";
        if ( StringUtils.isBlank ( payableDetailEntity.getSourceId () ) ) {
            if ( KingConstUtil.TAX_COST_AMOUNT_YES.equals ( payableEntity.getIsTaxCostAmount () ) ) {
                verifyAmount = CommonUtil.sumStr ( payableDetailEntity.getRateAmount (), payableDetailEntity.getNotTaxAmount (), 2 );
            } else if ( KingConstUtil.TAX_COST_AMOUNT_NO.equals ( payableEntity.getIsTaxCostAmount () ) ) {
                verifyAmount = CommonUtil.stringPoint ( payableDetailEntity.getNotTaxAmount (), 2 );
            }
            logEntity.setAllocateSources ( "应付单选单分配" );
        } else if ( payableDetailEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_PAYABLE ) {
            // 取成本差异金额
            verifyAmount = costDifferenceAmount ( payableEntity, payableDetailEntity );
            logEntity.setAllocateSources ( "费用自动调差" );
        }
        SupplierEntity supplierEntity = iSupplierService.getById ( payableEntity.getSupplierId () );
        //求金额本位币=金额乘以汇率
        logEntity.setVerifyAmount ( verifyAmount ).setVerifyAmountCurrency ( CommonUtil.rideStr ( payableEntity.getRate (), verifyAmount, 2 ) ).setCurrencyCode ( payableEntity.getCurrencyCode () ).
                setSupplierCode ( supplierEntity.getSupplierCode () ).setSupplierName ( supplierEntity.getSupplierName () );
        iKingDiffLogService.saveSingle ( logEntity );
        //2.生成关联的多条其他单据明细（入库单/出库单）
        String finalVerifyAmount = verifyAmount;
        if ( payableDetailEntity.getIsIncludeProcureCost () ) {
            //计入采购成本，查入库单
            KingInventoryEntity inventoryEntity = iKingInventoryService.getById ( payableDetailEntity.getInStorageNo () );
            List<KingInventoryMaterialEntity> list = iKingInventoryMaterialService.list ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
                    eq ( KingInventoryMaterialEntity :: getInventoryId, payableDetailEntity.getInStorageNo () ) );
            //多条入库单明细金额之和
            double sum = list.stream ().mapToDouble ( inventory -> Double.parseDouble ( inventory.getMoneyAmount () ) ).sum ();
            list.forEach ( inventoryDetail -> {
                // 遍历入库单明细
                KingDiffLogEntity logEntityBySource = new KingDiffLogEntity ();
                //出库单明细金额表
                //String amountMoneyBase = "0";
                //KingInventoryMaterialEntity inventoryMaterialEntity = iKingInventoryMaterialService.getOne ( new LambdaQueryWrapper<KingInventoryMaterialEntity> () );
                //if ( inventoryMaterialEntity != null ) {
                //    amountMoneyBase = inventoryMaterialEntity.getAmountMoneyBase ();
                //}
                //勾稽金额
                double weight = Double.parseDouble ( inventoryDetail.getMoneyAmount () ) / sum;
                logEntityBySource.setSerialNumber ( logEntity.getSerialNumber () ).setDocNo ( inventoryEntity.getDocCode () ).setLineNumber ( inventoryDetail.getLineNumber () ).setDocTypeCode ( inventoryEntity.getDocType () ).
                        setMaterialCode ( inventoryDetail.getMaterialCode () ).setVerifyNumber ( inventoryDetail.getReceiptsNum () ).setCurrencyCode ( inventoryEntity.getBaseCurrency () ).setBizTime ( inventoryEntity.getInventoryTime () ).
                        setSupplierCode ( inventoryEntity.getSupplierCode () ).setActualAllocationStandards ( KingConstUtil.AMOUNT_CURRENCY ).setAllocationWeight ( CommonUtil.doublePoint ( ( weight * 100 ), 0 ) + "%" ).
                        setVerifyAmount ( CommonUtil.rideStr ( finalVerifyAmount, String.valueOf ( weight ), 2 ) ).setAllocationBase ( inventoryDetail.getAmountMoneyBase () ).setStandardCurrencyCode ( deptEntity.getStandardCurrencyCode () ).
                        setMaterialId ( inventoryDetail.getMaterialId () ).setSourceMaterialDetailId ( inventoryDetail.getId () ).setBatchNum ( inventoryDetail.getBatchNum () ).setSupplierName ( supplierEntity.getSupplierName () );
                //勾稽金额本位币=勾稽金额*汇率
                logEntityBySource.setVerifyAmountCurrency ( CommonUtil.rideStr ( inventoryEntity.getExchangeRate(), logEntityBySource.getVerifyAmount (), 2 ) );
                iKingDiffLogService.saveSingle ( logEntityBySource );
                //检查是否需要生成成本调整单
                saveCostAdjustment ( logEntity, logEntityBySource );
            } );
        }
        //else if ( payableDetailEntity.getIsIncludeContractCost () ) {
            // 是否计入合同履约成本，源单是出库单
        //    KingInventoryEntity inventoryEntity = iKingInventoryService.getById ( payableDetailEntity.getOutStorageNo () );
            // 出库单明细
        //    List<KingInventoryMaterialEntity> list = iKingInventoryMaterialService.list (new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
        //            eq ( KingInventoryMaterialEntity::getInventoryId, payableDetailEntity.getOutStorageNo () ));
            // 多条出库单的明细金额之和
        //    double sum = list.stream ().mapToDouble ( inventory -> Double.parseDouble ( inventory.getMoneyAmount () ) ).sum ();
        //    list.forEach ( inventoryDetail -> {
        //        KingDiffLogEntity logEntityBySource = new KingDiffLogEntity ();
                //勾稽金额
        //        double weight = Double.parseDouble ( inventoryDetail.getMoneyAmount () ) / sum;
        //        logEntityBySource.setSerialNumber ( logEntity.getSerialNumber () ).setDocNo ( inventoryEntity.getDocCode () ).setLineNumber ( inventoryDetail.getLineNumber () ).
        //                setDocTypeCode ( inventoryEntity.getDocType () ).setMaterialCode ( inventoryDetail.getMaterialCode () ).setMaterialId ( inventoryDetail.getMaterialId () ).
        //                setVerifyNumber ( inventoryDetail.getReceiptsNum () ).setCurrencyCode ( inventoryEntity.getSettlementCurrency () ).setVerifyAmount ( CommonUtil.rideStr ( finalVerifyAmount, String.valueOf ( weight ), 2 ) ).
        //                setStandardCurrencyCode ( deptEntity.getStandardCurrencyCode () ).setVerifyAmountCurrency ( CommonUtil.rideStr ( inventoryEntity.getExchangeRate(), logEntityBySource.getVerifyAmount (), 2 ) ).
        //                setBizTime ( inventoryEntity.getInventoryTime () ).setSupplierCode ( inventoryEntity.getSupplierCode () ).setActualAllocationStandards ( KingConstUtil.AMOUNT_CURRENCY ).setSupplierName ( supplierEntity.getSupplierName () ).
        //                setAllocationWeight ( CommonUtil.doublePoint ( ( weight * 100 ), 0 ) + "%" ).setAllocationBase ( inventoryDetail.getAmountMoneyBase () ).setBatchNum ( inventoryDetail.getBatchNum () );
        //        iKingDiffLogService.saveSingle ( logEntityBySource );
        //    } );
        //}
    }

    /**
     * 生成差异日志（财务应付单），同步应付单 和 源单单据，根据各自物料明细
     * 注意：如果是标准应付单，本次勾稽金额应付单与源单金额相等；如果是费用应付单，本次勾稽金额则等于多条源单金额之和
     * @param payableEntity
     * @param payableDetail
     * @param differenceAmount
     */
    private void saveDiffLog (KingInvoicePayableEntity payableEntity, KingInvoicePayableDetailEntity payableDetail, String differenceAmount) {
        //1.通过应付单组装差异日志
        KingDiffLogEntity logEntityByPayable = new KingDiffLogEntity ();
        String serialNumber = String.format ( "%08d", 0 );
        KingDiffLogEntity one = iKingDiffLogService.getOne ( new LambdaQueryWrapper<KingDiffLogEntity> ().select ( KingDiffLogEntity :: getSerialNumber ).orderByDesc ( KingDiffLogEntity :: getSerialNumber ).last ( "LIMIT 1" ) );
        if ( one != null ) {
            serialNumber = one.getSerialNumber ();
        }
        //获取分公司汇率
        SysDept deptEntity = iSysDeptService.getById ( CurrentUserUtil.getCompanyId () );
        //供应商
        SupplierEntity supplierEntity = iSupplierService.getById ( payableEntity.getSupplierId () );
        logEntityByPayable.setSerialNumber ( String.format ( "%08d", ( Integer.parseInt ( serialNumber ) + 1 ) ) ).setDocNo ( payableEntity.getDocNo () ).setLineNumber ( payableDetail.getLineNumber () ).
                setVerifyNumber ( payableDetail.getNum () ).setCurrencyCode ( payableEntity.getCurrencyCode () ).setVerifyDate ( DateUtils.getNowDate () ).setDocTypeCode ( payableEntity.getDocTypeCode () ).
                setMaterialCode ( payableDetail.getMaterialCode () ).setSupplierCode ( supplierEntity.getSupplierCode () ).setBizTime ( payableEntity.getBizTime () ).setVerifyAmount ( differenceAmount ).
                setStandardCurrencyCode ( deptEntity.getStandardCurrencyCode () ).setVerifyAmountCurrency ( CommonUtil.rideStr ( payableEntity.getRate (), differenceAmount, 2 ) ).
                setMaterialId ( payableDetail.getMaterialId () ).setSourceMaterialDetailId ( payableDetail.getId () ).setSupplierName ( supplierEntity.getSupplierName () );
        iKingDiffLogService.saveSingle ( logEntityByPayable );
        //2.通过源单据组装差异日志
        KingDiffLogEntity logEntityByParent = new KingDiffLogEntity ();
        String sourceMaterialDetailId = payableDetail.getSourceMaterialDetailId ();
        //如果是红冲应付单，则找爷爷（入库单）
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_PAYABLE ) {
            KingInvoicePayableDetailEntity payableDetailEntity = iKingInvoicePayableDetailService.getOne ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().
                    eq ( KingInvoicePayableDetailEntity :: getId, payableDetail.getSourceMaterialDetailId () ) );
            sourceMaterialDetailId = payableDetailEntity.getSourceMaterialDetailId ();
        }
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_IN_STORAGE || payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_PAYABLE) {
            //入库单明细
            KingInventoryMaterialEntity inventoryMaterial = iKingInventoryMaterialService.getOne ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
                    eq ( KingInventoryMaterialEntity :: getId, sourceMaterialDetailId ) );
            if ( inventoryMaterial != null ) {
                //入库单主表
                KingInventoryEntity inventoryEntity = iKingInventoryService.getById ( inventoryMaterial.getInventoryId () );
                supplierEntity = iSupplierService.getById ( inventoryEntity.getSupplierId () );
                logEntityByParent.setDocNo ( inventoryEntity.getDocCode () ).setDocTypeCode ( inventoryEntity.getDocType () ).setLineNumber ( inventoryMaterial.getLineNumber () ).setVerifyAmount ( differenceAmount ).
                        setMaterialCode ( inventoryMaterial.getMaterialCode () ).setBizTime ( inventoryEntity.getInventoryTime () ).setVerifyNumber ( inventoryMaterial.getReceiptsNum () ).
                        setCurrencyCode ( inventoryEntity.getBaseCurrency () ).setSupplierCode ( inventoryEntity.getSupplierCode () ).setStandardCurrencyCode ( deptEntity.getStandardCurrencyCode () ).
                        setMaterialId ( inventoryMaterial.getMaterialId () ).setSourceMaterialDetailId ( inventoryMaterial.getId () ).setBatchNum ( inventoryMaterial.getBatchNum () ).setSupplierName ( supplierEntity.getSupplierName () );
                //计算勾稽金额本位币=金额*汇率
                logEntityByParent.setVerifyAmountCurrency ( CommonUtil.rideStr ( inventoryEntity.getExchangeRate (), differenceAmount, 2 ) );
            }
        }
        logEntityByParent.setSerialNumber ( logEntityByPayable.getSerialNumber () );
        logEntityByParent.setVerifyAmount ( differenceAmount );
        iKingDiffLogService.saveSingle ( logEntityByParent );
        //检查是否需要生成成本调整单
        saveCostAdjustment ( logEntityByPayable, logEntityByParent );
    }

    /**
     * 生成成本调整单：
     * 1、差异日志记录里同一个序号的，业务日期不在同一个月份的要生成成本调整单；
     * 2、差异日志记录里同一个序号的，业务日期在同一个月份的本次钩稽金额需要回写入入库单的当月物料成本，本次钩稽金额本位币需要回写入入库单的当月物料成本（本位币）中；（入库单查询自己算）
     * 3、生成成本调整单的时候 检查同物料同批号在本月以前有没有出库，如果有按出库数量除以总数量的比例生成出库成本调整单；
     * @param logEntity
     * @param logEntityByParent
     */
    private void saveCostAdjustment (KingDiffLogEntity logEntity, KingDiffLogEntity logEntityByParent) {
        if ( logEntity.getSerialNumber ().equals ( logEntityByParent.getSerialNumber () ) && !CommonUtil.checkSameMonth ( logEntity.getBizTime (), logEntityByParent.getBizTime () ) ) {
            KingCostAdjustmentEntity kingCostAdjustmentEntity = new KingCostAdjustmentEntity ();
            //获取时间大的月份
            int month = CommonUtil.getMonth ( CommonUtil.isAfter ( logEntity.getBizTime (), logEntityByParent.getBizTime () ) ? logEntityByParent.getBizTime () : logEntity.getBizTime () );
            kingCostAdjustmentEntity.setBizTime ( CommonUtil.setDateByMonth ( month ) );//两个源单中月份大的那个月的1号
            kingCostAdjustmentEntity.setMaterialCode ( logEntity.getMaterialCode () );
            kingCostAdjustmentEntity.setAdjustmentAmount ( logEntityByParent.getVerifyAmount () );
            kingCostAdjustmentEntity.setDocTypeCode ( KingConstUtil.KING_INVENTORY_COST );
            kingCostAdjustmentEntity.setType ( 1 );
            kingCostAdjustmentEntity.setBatchNumber ( logEntity.getBatchNum () );
            kingCostAdjustmentEntity.setSourceInventoryDocTypeCode ( logEntityByParent.getDocTypeCode () );
            kingCostAdjustmentEntity.setSourceInventoryDocTypeLine ( logEntityByParent.getLineNumber () );
            kingCostAdjustmentEntity.setSourcePayableDocTypeCode ( logEntity.getDocTypeCode () );
            kingCostAdjustmentEntity.setSourcePayableDocTypeLine ( logEntity.getLineNumber () );
            kingCostAdjustmentEntity.setAdjustmentReason ( "采购入库期初调差" );
            iKingCostAdjustmentService.saveSingle ( kingCostAdjustmentEntity );
            //根据物料和批号查询本月以前未生成成本调整单的出库记录
            KingInventoryMaterialEntity inventoryMaterialEntity = new KingInventoryMaterialEntity();
            inventoryMaterialEntity.setMaterialCode ( kingCostAdjustmentEntity.getMaterialCode () );
            inventoryMaterialEntity.setBatchNum ( kingCostAdjustmentEntity.getBatchNumber () );
            Date firstDayOfMonth = DateUtil.beginOfMonth ( new Date () );
            inventoryMaterialEntity.setCreateTime ( firstDayOfMonth ); // 本月1号
            List<KingInventoryMaterialEntity> list = iKingInventoryMaterialService.queryListByCostAdjustment ( inventoryMaterialEntity );
            list.forEach ( detail -> {
                KingCostAdjustmentEntity one = iKingCostAdjustmentService.getOne ( new LambdaQueryWrapper<KingCostAdjustmentEntity> ().eq ( KingCostAdjustmentEntity :: getInventoryId, detail.getId () ) );
                if ( one == null ) {
                    //之前没有生成出库成本调整单的记录
                    KingCostAdjustmentEntity kingCostAdjustment = new KingCostAdjustmentEntity ();
                    kingCostAdjustment.setBizTime ( CommonUtil.setDateByMonth ( month ) );
                    kingCostAdjustment.setMaterialCode ( detail.getMaterialCode () );
                    kingCostAdjustment.setAdjustmentAmount ( logEntityByParent.getVerifyAmount () );
                    kingCostAdjustment.setDocTypeCode ( KingConstUtil.KING_OUT_INVENTORY_COST );
                    kingCostAdjustment.setType ( 2 );
                    kingCostAdjustment.setSourceInventoryDocTypeCode ( kingCostAdjustmentEntity.getDocNo () );
                    kingCostAdjustment.setSourceInventoryDocTypeLine ( kingCostAdjustmentEntity.getSourceInventoryDocTypeLine () );
                    kingCostAdjustment.setAdjustmentReason ( "依据“期初入库成本调整单生成出库调整单”自动产生" );
                    iKingCostAdjustmentService.saveSingle ( kingCostAdjustment );
                }
            } );
        }
    }

    /**
     * 更新应付单/采购开票明细
     * @param kingInvoicePayableDetailEntities
     * @param payableId
     * @return
     */
    private int updatePayableDetail (List<KingInvoicePayableDetailEntity> kingInvoicePayableDetailEntities, String payableId, Integer sourceDocType) {
        if ( ! CollectionUtils.isEmpty ( kingInvoicePayableDetailEntities ) ) {
            String batchNumber = BatchNumberGenerator.generateBatchNumber();
            //删除已有明细
            iKingInvoicePayableDetailService.remove ( new LambdaUpdateWrapper<KingInvoicePayableDetailEntity> ().eq ( KingInvoicePayableDetailEntity::getInvoicePayableId, payableId ) );
            kingInvoicePayableDetailEntities.forEach ( payableDetailEntity -> {
                payableDetailEntity.setInvoicePayableId ( payableId );
                payableDetailEntity.setBatchNum ( batchNumber );
                payableDetailEntity.setSourceDocType ( sourceDocType );
                iKingInvoicePayableDetailService.insertKingInvoicePayableDetailEntity ( payableDetailEntity );
            } );
        }
        return 1;
    }

    /**
     * 处理应付单与付款计划明细
     * @param kingInvoicePayableProcurePlanEntities
     * @param payableId
     * @return
     */
    private int updatePayProcurePlan (List<KingInvoicePayableProcurePlanEntity> kingInvoicePayableProcurePlanEntities, String payableId) {
        if ( ! CollectionUtils.isEmpty ( kingInvoicePayableProcurePlanEntities ) ) {
            boolean remove = iKingInvoicePayableProcurePlanService.remove ( new LambdaUpdateWrapper<KingInvoicePayableProcurePlanEntity> ().eq ( KingInvoicePayableProcurePlanEntity::getInvoicePayableId, payableId ) );
            if ( ! remove ) {
                throw new CustomException ( "删除原采购开票付款计划失败！" );
            }
            kingInvoicePayableProcurePlanEntities.forEach ( payableProcurePlanEntity -> {
                payableProcurePlanEntity.setInvoicePayableId ( payableId );
                int i = iKingInvoicePayableProcurePlanService.insertKingInvoicePayableProcurePlanEntity ( payableProcurePlanEntity );
                if ( i == 0 ) {
                    throw new RuntimeException ( "更新/新增采购开票付款计划失败！" );
                }
            } );
        }
        return 1;
    }

    /**
     * 生成差异日志中，求应付单的成本差异金额的值
     * 应付单列表的成本差异金额计算分两种情况：
     *      1、应付单主表《税额计入成本》如果为true，则成本差异金额=（金额+税额-对应源单金额）*汇率
     *      2、应付单主表《税额计入成本》如果为false，则成本差异金额=（金额-对应源单金额）*汇率
     * 先拿源单信息，计算对应源单金额，在处理应付单明细金额
     * @param payableEntity：应付单对象
     * @param payableDetailEntity：应付单明细行
     * @return
     */
    private String costDifferenceAmount (KingInvoicePayableEntity payableEntity, KingInvoicePayableDetailEntity payableDetailEntity) {
        String difference = "0";
        Integer sourceDocType = payableDetailEntity.getSourceDocType ();
        //1.找源单，计算应付单列表的《对应源单金额》，对应源单金额=源单的实际金额/源单实际入库数量*应付单的开票数量
        String sourceAmount = "0"; //对应源单金额
        if ( sourceDocType == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_IN_STORAGE ) {
            KingInventoryMaterialEntity one = iKingInventoryMaterialService.getOne ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
                    eq ( KingInventoryMaterialEntity :: getId, payableDetailEntity.getSourceMaterialDetailId () ) );
            if ( one != null ) {
                sourceAmount = CommonUtil.rideStr ( CommonUtil.divisionStr ( one.getMoneyAmount (), one.getReceiptsNum (), 2 ), payableDetailEntity.getNum (), 2 );
            }
        }
        if ( payableEntity.getIsTaxCostAmount () ) {
            //成本差异金额=（金额+税额-对应源单金额）*汇率
            difference = CommonUtil.rideStr ( CommonUtil.subStr ( CommonUtil.sumStr ( payableDetailEntity.getNotTaxAmount (), payableDetailEntity.getRateAmount (), 2 ), sourceAmount, 2 ), payableEntity.getRate (), 2 );
        } else {
            //成本差异金额=（金额-对应源单金额）*汇率
            difference = CommonUtil.rideStr ( CommonUtil.subStr ( payableDetailEntity.getNotTaxAmount (), sourceAmount, 2 ), payableEntity.getRate (), 2 );
        }
        return difference;
    }

    /**
     * 计算源单金额
     * 1、如果源单是采购订单，则不用计算
     * 2、如果源单是入库单和退料单，则计算公式为：源单（不含税金额/数量）*应付单数量
     * 3、如果当前本单据是费用应付单：
     *   1）如果没有源单，则不计算
     *   2）如果有源单且不是采购订单：
     *       a）如果源单的税额计入成本，则=(源单税额+源单金额)/源单数量*当前应付单开票数量
     *       b）如果源单的税额不计入成本，则=(源单金额)/源单数量*当前应付单开票数量
     * @param payableEntity
     * @return
     */
    private String calSourceAmount ( KingInvoicePayableEntity payableEntity ) {
        //1.获取源单数据(入库单或退料单)
        String sourceAmount = "0";
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_IN_STORAGE ) {
            KingInventoryMaterialEntity one = iKingInventoryMaterialService.getOne ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
                    eq ( KingInventoryMaterialEntity :: getId, payableEntity.getSourceMaterialDetailId () ) );
            if ( one != null ) {
                // 计算公式=源单（不含税金额/数量）*应付单数量
                sourceAmount = CommonUtil.rideStr ( CommonUtil.divisionStr ( one.getMoneyAmount (), one.getReceiptsNum (), 2 ), payableEntity.getDetailNum (), 2 );
            }
        } else if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_REFUND ) {
            KingProcureRefundMaterialEntity one = iKingProcureRefundMaterialService.getOne ( new LambdaQueryWrapper<KingProcureRefundMaterialEntity> ().
                    eq ( KingProcureRefundMaterialEntity :: getId, payableEntity.getSourceMaterialDetailId () ) );
            if ( one != null ) {
                // 计算公式=源单（不含税金额/数量）*应付单数量
                sourceAmount = CommonUtil.rideStr ( CommonUtil.divisionStr ( one.getAmountMoney (), one.getRefundNum (), 2 ), payableEntity.getDetailNum (), 2 );
            }
        }
        //2.当前本单据是费用应付单，找源单，此时源单有且仅有一种：费用应付单
        if ( KingConstUtil.KING_PAYABLE_FEE.equals ( payableEntity.getDocTypeCode () ) ) {
            if ( payableEntity.getSourceDocType () != null && payableEntity.getSourceDocType () != KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_ORDER ) {
                KingInvoicePayableEntity one = this.getById ( payableEntity.getDetailSourceId () );
                KingInvoicePayableDetailEntity oneDetail = null;
                if ( one != null ) {
                    oneDetail = iKingInvoicePayableDetailService.getOne ( new LambdaQueryWrapper<KingInvoicePayableDetailEntity> ().eq ( KingInvoicePayableDetailEntity :: getInvoicePayableId, one.getId () ) );
                }
                if ( one != null && KingConstUtil.TAX_COST_AMOUNT_YES.equals ( one.getIsTaxCostAmount () ) ) {
                    // 计算公式=(源单税额+源单金额)/源单数量*当前应付单开票数量
                    sourceAmount = CommonUtil.divisionStr ( CommonUtil.sumStr ( oneDetail.getRateAmount (), oneDetail.getNotTaxAmount (), 2 ), payableEntity.getDetailNum (), 2 );
                } else if ( one != null && KingConstUtil.TAX_COST_AMOUNT_YES.equals ( one.getIsTaxCostAmount () ) ) {
                    // 计算公式=(源单金额)/源单数量*当前应付单开票数量
                    sourceAmount = CommonUtil.divisionStr ( oneDetail.getNotTaxAmount (), payableEntity.getDetailNum (), 2 );
                }
            }
        }
        return sourceAmount;
    }

    /**
     * 查询列表时计算成本差异金额，计算公式：
     * 公式1：应付单税额计入成本不打勾=（金额-对应源单金额）*汇率；
     * 公式2：应付单税额计入成本打勾=（金额+税额-对应源单金额）*汇率
     *
     * @param payableEntity
     * @return
     */
    private String calCostDifferenceAmount ( KingInvoicePayableEntity payableEntity ) {
        String costDifferenceAmount = "0";
        if ( "3".equals ( payableEntity.getAccountType () ) ) {
            if ( payableEntity.getIsTaxCostAmount () ) {
                costDifferenceAmount = CommonUtil.rideStr ( CommonUtil.subStr ( CommonUtil.sumStr ( payableEntity.getDetailNotTaxAmount (), payableEntity.getDetailRateAmount (), 2 ), payableEntity.getSourceAmount (), 2 ), payableEntity.getRate (), 2 );
            } else {
                costDifferenceAmount = CommonUtil.rideStr ( CommonUtil.subStr ( payableEntity.getDetailNotTaxAmount (), payableEntity.getSourceAmount (), 2 ), payableEntity.getRate (), 2 );
            }
        }
        return costDifferenceAmount;
    }

    /**
     * 计算对应源单价税合计
     * 计算公式=源单（价税合计/数量）*应付单数量
     * @param payableEntity
     * @return
     */
    private String calSourceTaxPriceAmount ( KingInvoicePayableEntity payableEntity ) {
        String sourceNum = "0", sourceTaxPriceAmount = "0";
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_IN_STORAGE ) {
            //1.获取入库单
            KingInventoryMaterialEntity one = iKingInventoryMaterialService.getOne ( new LambdaQueryWrapper<KingInventoryMaterialEntity> ().
                    eq ( KingInventoryMaterialEntity :: getId, payableEntity.getSourceMaterialDetailId () ) );
            if ( one != null ) {
                sourceNum = one.getReceiptsNum ();
                sourceTaxPriceAmount = one.getTotalTaxPrice ();
            }
        } else if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_REFUND ) {
            //2.获取退料单
            KingProcureRefundMaterialEntity one = iKingProcureRefundMaterialService.getOne ( new LambdaQueryWrapper<KingProcureRefundMaterialEntity> ().
                    eq ( KingProcureRefundMaterialEntity :: getId, payableEntity.getSourceMaterialDetailId () ) );
            if ( one != null ) {
                sourceNum = one.getRefundNum ();
                sourceTaxPriceAmount = one.getTotalTaxPrice ();
            }
        }
        String result = CommonUtil.rideStr ( CommonUtil.divisionStr ( sourceTaxPriceAmount, sourceNum, 2 ), payableEntity.getDetailNum (), 2 );
        return result;
    }

    /**
     * 计算源单汇率，直接取源单汇率
     * @param payableEntity
     * @return
     */
    private String calSourceRate ( KingInvoicePayableEntity payableEntity ) {
        String sourceRate = "0";
        if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_IN_STORAGE ) {
            KingInventoryEntity one = iKingInventoryService.getById ( payableEntity.getDetailSourceId () );
            if ( one != null ) {
                sourceRate = one.getExchangeRate ();
            }
        } else if ( payableEntity.getSourceDocType () == KingConstUtil.PAYABLE_SOURCE_DOC_TYPE_REFUND ) {
            KingProcureRefundEntity one = iKingProcureRefundService.getById ( payableEntity.getDetailSourceId () );
            if ( one != null ) {
                sourceRate = one.getExchangeRate ();
            }
        }
        return sourceRate;
    }

}
